package cachex

import (
	"context"
	"encoding/json"
	"errors"
	"github.com/redis/go-redis/v9"
	"log"
	"sync"
	"time"
)

type RedisBucket map[int32]*redis.Client

func (p RedisBucket) GetClient(ty int32) *redis.Client {
	i := ty % 16
	if client, ok := p[i]; ok {
		return client
	}
	return p[0]
}
func (p RedisBucket) ClientKey(ty int32, key string, handle func(client *redis.Client, key string)) {
	handle(p.GetClient(ty), key)
}

// 播放數
func IncrRecord(client *redis.Client, key string, expire time.Duration) error {
	background := context.Background()
	err := client.Incr(background, key).Err()
	if err != nil {
		return err
	}
	return client.Expire(background, key, expire).Err()
}
func CleanRecord(client *redis.Client, key string) error {
	return client.Del(context.Background(), key).Err()
}
func GetIncrRecord(client *redis.Client, key string) (int, int, error) {
	background := context.Background()
	count, err := client.Get(background, key).Int()
	if errors.Is(err, redis.Nil) {
		return 0, 0, nil
	}
	if err != nil {
		return 0, 0, err
	}
	result, err := client.TTL(background, key).Result()
	if err != nil {
		return 0, 0, err
	}
	return count, int(result.Minutes()), nil
}
func CountKey[T any](ty int32, key T) string {
	countKey := RedisCountKey[T]{Data: key, Type: ty}
	switch ty {

	}
	return countKey.Key()
}

type RedisCountKey[T any] struct {
	Data T     `json:"data,omitempty"`
	Type int32 `json:"type,omitempty"`
}

func (t RedisCountKey[T]) Key() string {
	marshal, err := json.Marshal(t)
	if err != nil {
		return ""
	}
	return string(marshal)
}

func Aopx[T any](isAdmin, flush bool, client *redis.Client, key string, duration time.Duration, handles ...func() (T, error)) (T, error) {
	var v T
	var err error
	ctx := context.Background()
	// 直接跳过
	if isAdmin {
		for _, handle := range handles {
			v, err = handle()
			if err != nil {
				_, err = client.Set(ctx, key, "", duration).Result()
				if err != nil {
					log.Println("Aopx", err)
				}
				return v, err
			}
		}
		return v, err
	}
	// 加互斥锁 缓存击穿
	if !flush {
		if result, err := client.Get(ctx, key).Bytes(); err == nil {
			if len(result) > 0 {
				if err = json.Unmarshal(result, &v); true {
					return v, err
				}
			} else {
				return v, errors.New("NotFound")
			}
		}
	}
	var mu sync.Mutex
	mu.Lock()
	defer mu.Unlock()
	for _, handle := range handles {
		v, err = handle()
		if err != nil {
			_, err = client.Set(ctx, key, "", duration).Result()
			if err != nil {
				log.Println("Aopx", err)
			}
			return v, err
		}
	}
	go func() {
		marshal, err := json.Marshal(v)
		if err != nil {
			log.Println("Aopx", err)
			return
		}
		_, err = client.Set(ctx, key, marshal, duration).Result()
		if err != nil {
			log.Println("Aopx", err)
			return
		}
	}()
	return v, err
}
